Chapter 9
Querying XML
1) Performing Set Operations on Node Sets
XSLT 1.0
<xsl:copy-of select="$node-set1 | $node-set2"/>
<xsl:copy-of select="$node-set1[count(. | $node-set2) = count($node-set2)]"/>
<xsl:copy-of select="$node-set1[count(. | $node-set2) != count($node-set2)]"/>
<xsl:copy-of select="$node-set1[count(. | $node-set2) != count($node-set2)] |
$node-set2[count(. | $node-set1) != count($node-set1)] "/>
<xsl:if test="count($ns1|$ns2) = count($ns1) and 
               count($ns1) = count($ns2)">
<xsl:if test="count($node-set1|$node-set2) = count($node-set1)">
<xsl:if test="count($ns1|$ns2) = count($ns1) and count($ns1) > count(ns2)">
Discussion
<xsl:variable name="males" select="//person[@sex='m']"/>
<xsl:variable name="females" select="//person[@sex='f']"/>
<xsl:variable name="smokers" select="//person[@smoker='yes']"/>
<xsl:variable name="non-smokers" select="//person[@smoker='no']"/>
<!-- Male smokers -->
<xsl:variable name="super-risk" 
     select="$males[count(. | $smokers) = count($smokers)]"/>
<!-- Female smokers -->
<xsl:variable name="high-risk" 
     select="$females[count(. | $smokers) = count($smokers)]"/>
<!-- Male non-smokers -->
<xsl:variable name="moderate-risk" 
     select="$males[count(. | $non-smokers) = count($non-smokers)]"/>
<!-- Female non-smokers -->
<xsl:variable name="low-risk" 
     select="$females[count(. | $non-smokers) = count($non-smokers)]"/>
<!-- Male smokers -->
<xsl:variable name="super-risk" 
     select="//person[@sex='m' and @smoker='y']"/>
<!-- Female smokers -->
<xsl:variable name="high-risk" 
     select="//person[@sex='f' and @smoker='y']"/>
<!-- Male non-smokers -->
<xsl:variable name="moderate-risk" 
     select="//person[@sex='m' and @smoker='n']"/>
<!-- Female non-smokers -->
<xsl:variable name="low-risk" 
     select="//person[@sex='f' and @smoker='n']"/>
<!-- Male smokers -->
<xsl:variable name="super-risk" 
     select="$males[@smoker='y']"/>
<!-- Female smokers -->
<xsl:variable name="high-risk" 
     select="$females[@smoker='y']"/>
<!-- Male non-smokers -->
<xsl:variable name="moderate-risk" 
     select="$males[@smoker='n']"/>
<!-- Female non-smokers -->
<xsl:variable name="low-risk" 
     select="$females[@smoker='n']"/>
<!-- All elements that have elements c1 or c2  as children-->
<xsl:variable name="set1" select="//*[c1 or c2]"/>
<!-- All elements that have elements c3 and c4  as children-->
<xsl:variable name="set2" select="//*[c3 and c4]"/>
<!-- All elements whose parent has attribute a1-->
<xsl:variable name="set3" select="//*[../@a1]"/>
<!-- All elements whose parent has attribute a2-->
<xsl:variable name="set4" select="//*[../@a2]"/>
2) Performing Set Operations on Node Sets 
Using Value Semantics
XSLT 1.0
Example 9-1. people1.xslt 
<people>
  <person name="Brad York" age="38" sex="m" smoker="yes"/>
  <person name="Charles Xavier" age="32" sex="m" smoker="no"/>
  <person name="David Willimas" age="33" sex="m" smoker="no"/>
</people>
Example 9-2. people2.xslt 
<people>
  <person name="Al Zehtooney" age="33" sex="m" smoker="no"/>
  <person name="Brad York" age="38" sex="m" smoker="yes"/>
  <person name="Charles Xavier" age="32" sex="m" smoker="no"/>
</people>
Example 9-3. Failed attempt to use XSLT union to select unique people 
<xsl:template match="/">
  <people>
    <xsl:copy-of select="//person | document('people2.xml')//person"/>
  </people>
</xsl:template>
Example 9-4. Output when run with people1.xml as input 
<people>
   <person name="Brad York" age="38" sex="m" smoker="yes"/>
   <person name="Charles Xavier" age="32" sex="m" smoker="no"/>
   <person name="David Willimas" age="33" sex="m" smoker="no"/>
   <person name="Al Zehtooney" age="33" sex="m" smoker="no"/>
   <person name="Brad York" age="38" sex="m" smoker="yes"/>
   <person name="Charles Xavier" age="32" sex="m" smoker="no"/>
</people>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:vset="http:/www.ora.com/XSLTCookbook/namespaces/vset">
 
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
   
<!-- The default implementation of element equality. Override in the importing 
stylesheet as neccessary. -->
<xsl:template match="node() | @*" mode="vset:element-equality">
  <xsl:param name="other"/>
  <xsl:if test=". = $other">  
    <xsl:value-of select="true()"/>
  </xsl:if>
</xsl:template>
   
<!-- The default set membership test uses element equality. You will rarely need to 
override this in the importing stylesheet. -->
<xsl:template match="node() | @*" mode="vset:member-of">
  <xsl:param name="elem"/>
  <xsl:variable name="member-of">
    <xsl:for-each select=".">
      <xsl:apply-templates select="." mode="vset:element-equality">
        <xsl:with-param name="other" select="$elem"/>
      </xsl:apply-templates>
    </xsl:for-each>
  </xsl:variable>
  <xsl:value-of select="string($member-of)"/>
</xsl:template>
   
<!-- Compute the union of two sets using "by value" equality. -->
<xsl:template name="vset:union">
  <xsl:param name="nodes1" select="/.." />
  <xsl:param name="nodes2" select="/.." />
  <!-- for internal use -->
  <xsl:param name="nodes" select="$nodes1 | $nodes2" />
  <xsl:param name="union" select="/.." />
  <xsl:choose>
    <xsl:when test="$nodes">
      <xsl:variable name="test">
        <xsl:apply-templates select="$union" mode="vset:member-of">
          <xsl:with-param name="elem" select="$nodes[1]" />
        </xsl:apply-templates>
      </xsl:variable>
      <xsl:call-template name="vset:union">
        <xsl:with-param name="nodes" select="$nodes[position() > 1]" />
        <xsl:with-param name="union" 
                        select="$union | $nodes[1][not(string($test))]" />
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:apply-templates select="$union" mode="vset:union" />
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>
   
<!-- Return a copy of union by default. Override in importing stylesheet  to 
recieve
reults as a "callback"-->
<xsl:template match="/ | node() | @*" mode="vset:union">
  <xsl:copy-of select="."/>
</xsl:template>
   
<!-- Compute the intersection of two sets using "by value" equality. -->
<xsl:template name="vset:intersection">
  <xsl:param name="nodes1" select="/.."/>
  <xsl:param name="nodes2" select="/.."/>
  <!-- For internal use -->
  <xsl:param name="intersect" select="/.."/>
  
  <xsl:choose>
    <xsl:when test="not($nodes1)">
      <xsl:apply-templates select="$intersect" mode="vset:intersection"/>
    </xsl:when>
    <xsl:when test="not($nodes2)">
      <xsl:apply-templates select="$intersect" mode="vset:intersection"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="test1">
        <xsl:apply-templates select="$nodes2" mode="vset:member-of">
          <xsl:with-param name="elem" select="$nodes1[1]"/>
        </xsl:apply-templates>
      </xsl:variable>
      <xsl:variable name="test2">
        <xsl:apply-templates select="$intersect" mode="vset:member-of">
          <xsl:with-param name="elem" select="$nodes1[1]"/>
        </xsl:apply-templates>
      </xsl:variable>
      <xsl:choose>
        <xsl:when test="string($test1) and not(string($test2))">
          <xsl:call-template name="vset:intersection">
            <xsl:with-param name="nodes1" 
                    select="$nodes1[position() > 1]"/>
            <xsl:with-param name="nodes2" select="$nodes2"/>
            <xsl:with-param name="intersect" 
                    select="$intersect | $nodes1[1]"/>
          </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
          <xsl:call-template name="vset:intersection">
            <xsl:with-param name="nodes1" 
                    select="$nodes1[position() > 1]"/>
            <xsl:with-param name="nodes2" select="$nodes2"/>
            <xsl:with-param name="intersect" select="$intersect"/>
          </xsl:call-template>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>
   
<!-- Return a copy of intersection by default. Override in importing stylesheet to
recieve results as a "callback"-->
<xsl:template match="/ | node() | @*" mode="vset:intersection">
  <xsl:copy-of select="."/>
</xsl:template>
   
<!-- Compute the differnce between two sets (node1 - nodes2) using "by value" 
equality. -->
<xsl:template name="vset:difference">
  <xsl:param name="nodes1" select="/.."/>
  <xsl:param name="nodes2" select="/.."/>
  <!-- For internal use -->
  <xsl:param name="difference" select="/.."/>
  
  <xsl:choose>
    <xsl:when test="not($nodes1)">
      <xsl:apply-templates select="$difference" mode="vset:difference"/>
    </xsl:when>
    <xsl:when test="not($nodes2)">
      <xsl:apply-templates select="$nodes1" mode="vset:difference"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="test1">
        <xsl:apply-templates select="$nodes2" mode="vset:member-of">
          <xsl:with-param name="elem" select="$nodes1[1]"/>
        </xsl:apply-templates>
      </xsl:variable>
      <xsl:variable name="test2">
        <xsl:apply-templates select="$difference" mode="vset:member-of">
          <xsl:with-param name="elem" select="$nodes1[1]"/>
        </xsl:apply-templates>
      </xsl:variable>
      <xsl:choose>
        <xsl:when test="string($test1) or string($test2)">
          <xsl:call-template name="vset:difference">
            <xsl:with-param name="nodes1" 
                    select="$nodes1[position() > 1]"/>
            <xsl:with-param name="nodes2" select="$nodes2"/>
            <xsl:with-param name="difference" select="$difference"/>
          </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
          <xsl:call-template name="vset:difference">
            <xsl:with-param name="nodes1" 
                    select="$nodes1[position() > 1]"/>
            <xsl:with-param name="nodes2" select="$nodes2"/>
            <xsl:with-param name="difference" 
                    select="$difference | $nodes1[1]"/>
          </xsl:call-template>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>
   
<!-- Return a copy of difference by default. Override in importing stylesheet to
recieve results as a "callback"-->
<xsl:template match="/ | node() | @*" mode="vset:difference">
  <xsl:copy-of select="."/>
</xsl:template>
Union(nodes1,nodes2)
The union includes everything in nodes2 plus everything in nodes1 not already a 
member of nodes2. 
Intersection(nodes1,nodes2)
The intersection includes everything in nodes1 that is also a member of nodes2. 
Difference(nodes1,nodes2)
The difference includes everything in nodes1 that is not also a member of 
nodes2. 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:vset="http:/www.ora.com/XSLTCookbook/namespaces/vset">
   
<xsl:import href="set.ops.xslt"/>
   
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
   
<xsl:template match="/">
  <people>
    <xsl:call-template name="vset:union">
      <xsl:with-param name="nodes1" select="//person"/>
      <xsl:with-param name="nodes2" select="document('people2.xml')//person"/>
    </xsl:call-template>
  </people>
</xsl:template>
   
<!--Define person equality as having the same name -->
<xsl:template match="person" mode="vset:element-equality">
  <xsl:param name="other"/>
  <xsl:if test="@name = $other/@name">  
    <xsl:value-of select="true()"/>
  </xsl:if>
</xsl:template>
   
</xsl:stylesheet>
XSLT 2.0
<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:xs="http://www.w3.org/2001/XMLSchema" 
xmlns:vset="http:/www.ora.com/XSLTCookbook/namespaces/vset">

<!-- The default implementation of element equality. Override in the importing 
stylesheet as neccessary. -->
<xsl:function name="vset:element-equality" as="xs:boolean">
  <xsl:param name="item1" as="item()?"/>
  <xsl:param name="item2" as="item()?"/>
  <xsl:sequence select="$item1 = $item2"/>
</xsl:function>
   
<!-- The default set membership test uses element equality. You will rarely need to 
override this in the importing stylesheet. -->
<xsl:function name="vset:member-of" as="xs:boolean">
  <xsl:param name="set" as="item()*"/>
  <xsl:param name="elem" as="item()"/>
  <xsl:variable name="member-of" as="xs:boolean*" 
                       select="for $test in $set 
                                    return if (vset:element-equality($test, $elem)) 
                                               then true() else ()"/>
  <xsl:sequence select="not(empty($member-of))"/>
</xsl:function>
   
<!-- Compute the union of two sets using "by value" equality. -->
<xsl:function name="vset:union" as="item()*">
  <xsl:param name="nodes1" as="item()*"  />
  <xsl:param name="nodes2" as="item()*" />
 <xsl:sequence select="$nodes1, for $test in $nodes2 
                                return if (vset:member-of($nodes1,$test)) 
                                       then () else $test"/>  
</xsl:function>
   
   
<!-- Compute the intersection of two sets using "by value" equality. -->
<xsl:function name="vset:intersection" as="item()*">
  <xsl:param name="nodes1" as="item()*" />
  <xsl:param name="nodes2" as="item()*" />
 <xsl:sequence select="for $test in $nodes1 
                       return if (vset:member-of($nodes2,$test)) 
                              then $test else ()"/>  
</xsl:function>
   
<!-- Compute the differnce between two sets (node1 - nodes2) using "by value" 
equality. -->

<xsl:function name="vset:difference" as="item()*">
  <xsl:param name="nodes1" as="item()*" />
  <xsl:param name="nodes2" as="item()*" />
 <xsl:sequence select="for $test in $nodes1 return if (vset:member-
of($nodes2,$test)) then () else $test"/>  
</xsl:function>


</xsl:stylesheet>
Discussion
Example 9-5. doc1.xml 
<doc xmlns:doc1="doc1" xmlns="doc1">
  <chapter label="1">
    <section label="1">
      <p>
        Once upon a time...
      </p>
    </section>
  </chapter>
  <chapter label="2">
    <note to="editor">I am still waiting for my $100000 advance.</note>
    <section label="1">
      <p>
        ... and they lived happily ever after.
      </p>
    </section>
  </chapter>
</doc>
Example 9-6. doc2.xml 
<doc xmlns:doc1="doc2" xmlns="doc2">
  <chapter label="1">
    <section label="1">
      <sub>
        <p>
          Once upon a time...
          <ref type="footnote" label="1"/>
        </p>
      </sub>
      <fig>Figure1</fig>
    </section>
    <footnote label="1">
      Hey diddle diddle.
    </footnote>
  </chapter>
  <chapter label="2">
    <section label="1">
      <p>
        ... and they lived happily ever after.
      </p>
    </section>
  </chapter>
</doc>
Example 9-7. unique-element-names.xslt 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:doc1="doc1" xmlns:doc2="doc2"
  xmlns:vset="http:/www.ora.com/XSLTCookbook/namespaces/vset"
  extension-element-prefixes="vset">
   
  <xsl:import href="set.ops.xslt"/> 
   
  <xsl:output method="text" />
  
  <xsl:template match="/">
    <xsl:text>&#xa;The elements in common are: </xsl:text>
    <xsl:call-template name="vset:intersection">
      <xsl:with-param name="nodes1" select="//*"/>
      <xsl:with-param name="nodes2" select="document('doc2.xml')//*"/>
    </xsl:call-template>  
   
    <xsl:text>&#xa;The elements only in doc1 are: </xsl:text>
    <xsl:call-template name="vset:difference">
      <xsl:with-param name="nodes1" select="//*"/>
      <xsl:with-param name="nodes2" select="document('doc2.xml')//*"/>
    </xsl:call-template>  
   
    <xsl:text>&#xa;The elements only in doc2 are: </xsl:text>
    <xsl:call-template name="vset:difference">
      <xsl:with-param name="nodes1" select="document('doc2.xml')//*"/>
      <xsl:with-param name="nodes2" select="//*"/>
    </xsl:call-template>  
    <xsl:text>&#xa;</xsl:text>
   
  </xsl:template>
  
  <xsl:template match="*" mode="vset:intersection">
     <xsl:value-of select="local-name(.)"/>
     <xsl:if test="position() != last()">
       <xsl:text>, </xsl:text>
     </xsl:if>
  </xsl:template>
   
  <xsl:template match="*" mode="vset:difference">
     <xsl:value-of select="local-name(.)"/>
     <xsl:if test="position() != last()">
       <xsl:text>, </xsl:text>
     </xsl:if>
  </xsl:template>
           
  <xsl:template match="doc1:* | doc2:*" mode="vset:element-equality">
   <xsl:param name="other"/>
    <xsl:if test="local-name(.) = local-name($other)">  
      <xsl:value-of select="true()"/>
    </xsl:if>
  </xsl:template>
      
</xsl:stylesheet>
Example 9-8. Output 
The elements in common are: doc, chapter, section, p
The elements only in doc1 are: note
The elements only in doc2 are: sub, ref, fig, footnote
 

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:vxd="urn:schemas-microsoft-com:office:visio" 
  xmlns:vset="http:/www.ora.com/XSLTCookbook/namespaces/vset" 
  extension-element-prefixes="vset">
   
  <xsl:import href="set.ops.xslt"/> 
   
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
     
  <xsl:template match="/">
  <UniqueShapes>
      <xsl:call-template name="vset:intersection">
        <xsl:with-param name="nodes1" select="//vxd:Pages/*/*/vxd:Shape"/>
        <xsl:with-param name="nodes2" select="//vxd:Pages/*/*/vxd:Shape"/>
      </xsl:call-template> 
    </UniqueShapes>
  </xsl:template>
  
  <xsl:template match="vxd:Shape" mode="vset:intersection">
    <xsl:copy-of select="." />
  </xsl:template>
   
<xsl:template match="vxd:Shape" mode="vset:element-equality">
  <xsl:param name="other"/>
  <xsl:choose>
    <xsl:when test="@Master and $other/@Master and @Master = $other/@Master">
      <xsl:value-of select="true()"/>
    </xsl:when>
    <xsl:when test="not(@Master) or not($other/@Master)">
      <xsl:variable name="geom1">
        <xsl:for-each select="vxd:Geom//*/@*">
          <xsl:sort select="name()"/>
          <xsl:value-of select="."/>
        </xsl:for-each>
     </xsl:variable> 
      <xsl:variable name="geom2">
        <xsl:for-each select="$other/vxd:Geom//*/@*">
          <xsl:sort select="name()"/>
          <xsl:value-of select="."/>
        </xsl:for-each>
     </xsl:variable> 
      <xsl:if test="$geom1 = $geom2">
        <xsl:value-of select="true()"/>
      </xsl:if>
    </xsl:when>
  </xsl:choose>
</xsl:template>
 
 
</xsl:stylesheet>
3) Determining Set Equality by Value
<xsl:template name="vset:equal-text-values">
  <xsl:param name="nodes1" select="/.."/>
  <xsl:param name="nodes2" select="/.."/>
  <xsl:choose>
   <!--Empty node-sets have equal values -->
    <xsl:when test="not($nodes1) and not($nodes2)">
      <xsl:value-of select="true()"/>
      </xsl:when>
    <!--Node sets of unequal sizes can not have equal values -->
    <xsl:when test="count($nodes1) != count($nodes2)"/>
    <!--If an element of nodes1 is present in nodes2 then the node sets 
     have equal values if the node sets without the common element have equal 
     values -->
    <xsl:when test="$nodes1[1] = $nodes2">
      <xsl:call-template name="vset:equal-text-values">
          <xsl:with-param name="nodes1" select="$nodes1[position()>1]"/>
          <xsl:with-param name="nodes2" 
                         select="$nodes2[not(. = $nodes1[1])]"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise/>
  </xsl:choose>
</xsl:template>
<?xml version="1.0" encoding="UTF-8"?>
<library>
  <book>
    <name>High performance Java programming.</name>
    <borrowers>
      <borrower>James Straub</borower> 
    </borrowers>
  </book>
  <book>
    <name>Exceptional C++</name>
    <borrowers>
      <borrower>Steven Levitt</borower> 
    </borrowers>
  </book>
  <book>
    <name>Design Patterns</name>
    <borrowers>
      <borrower>Steven Levitt</borower> 
      <borrower>James Straub</borower> 
      <borrower>Steven Levitt</borower> 
    </borrowers>
  </book>
  <book>
    <name>The C++ Programming Language</name>
    <borrowers>
      <borrower>James Straub</borower> 
      <borrower>James Straub</borower> 
      <borrower>Steven Levitt</borower> 
    </borrowers>
  </book>
</library>
<xsl:template name="vset:equal-text-values-ignore-dups">
  <xsl:param name="nodes1" select="/.."/>
  <xsl:param name="nodes2" select="/.."/>
  <xsl:choose>
   <!--Empty node-sets have equal values -->
    <xsl:when test="not($nodes1) and not($nodes2)">
      <xsl:value-of select="true()"/>
      </xsl:when>
    <!--If an element of nodes1 is present in nodes2 then the node sets 
     have equal values if the node sets without the common element have equal 
     values -->
    <!delete this line 
          <xsl:when test="count($nodes1) != count($nodes2)"/> >
    <xsl:when test="$nodes1[1] = $nodes2">
      <xsl:call-template name="vset:equal-text-values">
          <xsl:with-param name="nodes1" 
                         select="$nodes1[not(. = $nodes1[1])]"/>
          <xsl:with-param name="nodes2" 
               select="$nodes2[not(. = $nodes1[1])]"/>      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise/>
  </xsl:choose>
</xsl:template>
<xsl:template name="vset:equal">
  <xsl:param name="nodes1" select="/.."/>
  <xsl:param name="nodes2" select="/.."/>
  <xsl:if test="count($nodes1) = count($nodes2)">
    <xsl:call-template name="vset:equal-impl">
      <xsl:with-param name="nodes1" select="$nodes1"/>
      <xsl:with-param name="nodes2" select="$nodes2"/>
    </xsl:call-template>
  </xsl:if>
</xsl:template>
   
<!-- Once we know the sets have the same number of elements -->
<!-- we only need to test that every member of the first set is -->
<!-- a member of the second -->
<xsl:template name="vset:equal-impl">
  <xsl:param name="nodes1" select="/.."/>
  <xsl:param name="nodes2" select="/.."/>
  <xsl:choose>
    <xsl:when test="not($nodes1)">
      <xsl:value-of select="true()"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="test">
        <xsl:apply-templates select="$nodes2" mode="vset:member-of">
          <xsl:with-param name="elem" select="$nodes1[1]"/>
        </xsl:apply-templates>
      </xsl:variable>
      <xsl:if test="string($test)">
        <xsl:call-template name="vset:equal-impl">
          <xsl:with-param name="nodes1" select="$nodes1[position() > 1]"/>
          <xsl:with-param name="nodes2" select="$nodes2"/>
        </xsl:call-template>
      </xsl:if>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>
<xsl:template name="vset:equal-ignore-dups">
  <xsl:param name="nodes1" select="/.."/>
  <xsl:param name="nodes2" select="/.."/>
  
  <xsl:variable name="mismatch1">
    <xsl:for-each select="$nodes1">
      <xsl:variable name="test-elem">
        <xsl:apply-templates select="$nodes2" mode="vset:member-of">
          <xsl:with-param name="elem" select="."/>
        </xsl:apply-templates>
      </xsl:variable>
      <xsl:if test="not(string($test-elem))">
        <xsl:value-of select=" 'false' "/>
      </xsl:if>
    </xsl:for-each>
  </xsl:variable>
  <xsl:if test="not($mismatch1)">
    <xsl:variable name="mismatch2">
      <xsl:for-each select="$nodes2">
        <xsl:variable name="test-elem">
          <xsl:apply-templates select="$nodes1" mode="vset:member-of">
            <xsl:with-param name="elem" select="."/>
          </xsl:apply-templates>
        </xsl:variable>
        <xsl:if test="not(string($test-elem))">
          <xsl:value-of select=" 'false' "/>
        </xsl:if>
      </xsl:for-each>
    </xsl:variable>
    <xsl:if test="not($mismatch2)">
      <xsl:value-of select="true()"/>
    </xsl:if>
  </xsl:if>
</xsl:template>
4) Performing Structure-Preserving Queries
Example 9-9. copy.xslt 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   
<xsl:template match="node() | @*">
  <xsl:copy>
    <xsl:apply-templates select="@* | node()"/>
  </xsl:copy>
</xsl:template>
   
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   
     <xsl:import href="copy.xslt"/>
   
    <!-- Collapse space left by removing person elements -->
    <xsl:strip-space elements="people"/>
      
     <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
     
     <xsl:template match="person[@sex = 'f' and @smoker='yes']">
       <!-- Apply default behavior, which is to copy -->
       <xsl:apply-imports/>
     </xsl:template>
   
     <!-- Ignore other people -->
     <xsl:template match="person"/>
     
</xsl:stylesheet>
<xsl:template match="person[@sex != 'f' or @smoker != 'yes']" />
Discussion
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   
     <xsl:import href="copy.xslt"/>
   
     <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
     
     <xsl:template match="@sex">
       <xsl:attribute name="gender">
          <xsl:value-of select="."/>
       </xsl:attribute>
     </xsl:template>
   
</xsl:stylesheet>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   
     <xsl:import href="copy.xslt"/>
   
     <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
     
     <xsl:template match="@sex">
       <xsl:attribute name="gender">
          <xsl:value-of select="."/>
       </xsl:attribute>
     <xsl:apply-imports/>
     </xsl:template>
   
</xsl:stylesheet>
     
5) Joins
<database>
  <suppliers>
    <supplier id="S1" name="Smith" status="20" city="London"/>
    <supplier id="S2" name="Jones" status="10" city="Paris"/>
    <supplier id="S3" name="Blake" status="30" city="Paris"/>
    <supplier id="S4" name="Clark" status="20" city="London"/>
    <supplier id="S5" name="Adams" status="30" city="Athens"/>
  </suppliers>
  <parts>
    <part id="P1" name="Nut" color="Red" weight="12" city="London"/>
    <part id="P2" name="Bult" color="Green" weight="17" city="Paris"/>
    <part id="P3" name="Screw" color="Blue" weight="17" city="Rome"/>
    <part id="P4" name="Screw" color="Red" weight="14" city="London"/>
    <part id="P5" name="Cam" color="Blue" weight="12" city="Paris"/>
    <part id="P6" name="Cog" color="Red" weight="19" city="London"/>
  </parts>
  <inventory>
    <invrec sid="S1" pid="P1" qty="300"/>
    <invrec sid="S1" pid="P2" qty="200"/>
    <invrec sid="S1" pid="P3" qty="400"/>
    <invrec sid="S1" pid="P4" qty="200"/>
    <invrec sid="S1" pid="P5" qty="100"/>
    <invrec sid="S1" pid="P6" qty="100"/>
    <invrec sid="S2" pid="P1" qty="300"/>
    <invrec sid="S2" pid="P2" qty="400"/>
    <invrec sid="S3" pid="P2" qty="200"/>
    <invrec sid="S4" pid="P2" qty="200"/>
    <invrec sid="S4" pid="P4" qty="300"/>
    <invrec sid="S4" pid="P5" qty="400"/>
  </inventory>
</database>
<xsl:template match="/">
  <result>
    <xsl:for-each select="database/suppliers/*">
      <xsl:variable name="supplier" select="."/>
      <xsl:for-each select="/database/parts/*[@city=current()/@city]">
      <colocated>
        <xsl:copy-of select="$supplier"/>
        <xsl:copy-of select="."/>
      </colocated>
      </xsl:for-each>
    </xsl:for-each>
  </result>
</xsl:template>
<xsl:template match="/">
  <result>
    <xsl:apply-templates select="database/suppliers/supplier" />
  </result>
</xsl:template>
   
<xsl:template match="supplier">
  <xsl:apply-templates select="/database/parts/part[@city = current()/@city]">
    <xsl:with-param name="supplier" select="." />
  </xsl:apply-templates>
</xsl:template>
   
<xsl:template match="part">
  <xsl:param name="supplier" select="/.." />
  <colocated>
    <xsl:copy-of select="$supplier" />
    <xsl:copy-of select="." />
  </colocated>
</xsl:template>
<xsl:key name="part-city" match="part" use="@city"/>
   
<xsl:template match="/">
  <result>
    <xsl:for-each select="database/suppliers/*">
      <xsl:variable name="supplier" select="."/>
      <xsl:for-each select="key('part-city',$supplier/@city)">
      <colocated>
        <xsl:copy-of select="$supplier"/>
        <xsl:copy-of select="."/>
      </colocated>
      </xsl:for-each>
    </xsl:for-each>
  </result>
</xsl:template>
<result>
   <colocated>
      <supplier id="S1" name="Smith" status="20" city="London"/>
      <part id="P1" name="Nut" color="Red" weight="12" city="London"/>
   </colocated>
   <colocated>
      <supplier id="S1" name="Smith" status="20" city="London"/>
      <part id="P4" name="Screw" color="Red" weight="14" city="London"/>
   </colocated>
   <colocated>
      <supplier id="S1" name="Smith" status="20" city="London"/>
      <part id="P6" name="Cog" color="Red" weight="19" city="London"/>
   </colocated>
   <colocated>
      <supplier id="S2" name="Jones" status="10" city="Paris"/>
      <part id="P2" name="Bult" color="Green" weight="17" city="Paris"/>
   </colocated>
   <colocated>
      <supplier id="S2" name="Jones" status="10" city="Paris"/>
      <part id="P5" name="Cam" color="Blue" weight="12" city="Paris"/>
   </colocated>
   <colocated>
      <supplier id="S3" name="Blake" status="30" city="Paris"/>
      <part id="P2" name="Bult" color="Green" weight="17" city="Paris"/>
   </colocated>
   <colocated>
      <supplier id="S3" name="Blake" status="30" city="Paris"/>
      <part id="P5" name="Cam" color="Blue" weight="12" city="Paris"/>
   </colocated>
   <colocated>
      <supplier id="S4" name="Clark" status="20" city="London"/>
      <part id="P1" name="Nut" color="Red" weight="12" city="London"/>
   </colocated>
   <colocated>
      <supplier id="S4" name="Clark" status="20" city="London"/>
      <part id="P4" name="Screw" color="Red" weight="14" city="London"/>
   </colocated>
   <colocated>
      <supplier id="S4" name="Clark" status="20" city="London"/>
      <part id="P6" name="Cog" color="Red" weight="19" city="London"/>
   </colocated>
</result>
Discussion
XSLT 1.0
<xsl:template match="/">
  <result>
    <xsl:for-each select="database/suppliers/*">
      <xsl:variable name="supplier" select="."/>
       <! This does not work! >
      <xsl:for-each select="/database/parts/*[current( )/@city > @city]">
      <colocated>
        <xsl:copy-of select="$supplier"/>
        <xsl:copy-of select="."/>
      </colocated>
      </xsl:for-each>
    </xsl:for-each>
  </result>
</xsl:template>
<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
   
<xsl:variable name="unique-cities" 
     select="//@city[not(. = ../preceding::*/@city)]"/>
   
<xsl:variable name="city-ordering">
  <xsl:for-each select="$unique-cities">
    <xsl:sort select="."/>
    <city name="{.}" order="{position()}"/>
  </xsl:for-each>
</xsl:variable>      
   
<xsl:template match="/">
  <result>
    <xsl:for-each select="database/suppliers/*">
      <xsl:variable name="s" select="."/>
      <xsl:for-each select="/database/parts/*">
        <xsl:variable name="p" select="."/>
        <xsl:if 
          test="$city-ordering/*[@name = $s/@city]/@order &gt; 
               $city-ordering/*[@name = $p/@city]/@order">
          <supplier-city-follows-part-city>
            <xsl:copy-of select="$s"/>
            <xsl:copy-of select="$p"/>
          </supplier-city-follows-part-city>
        </xsl:if>
      </xsl:for-each>
    </xsl:for-each>
  </result>
</xsl:template>
  
</xsl:stylesheet>
<result>
   <supplier-city-follows-part-city>
      <supplier id="S2" name="Jones" status="10" city="Paris"/>
      <part id="P1" name="Nut" color="Red" weight="12" city="London"/>
   </supplier-city-follows-part-city>
   <supplier-city-follows-part-city>
      <supplier id="S2" name="Jones" status="10" city="Paris"/>
      <part id="P4" name="Screw" color="Red" weight="14" city="London"/>
   </supplier-city-follows-part-city>
   <supplier-city-follows-part-city>
      <supplier id="S2" name="Jones" status="10" city="Paris"/>
      <part id="P6" name="Cog" color="Red" weight="19" city="London"/>
   </supplier-city-follows-part-city>
   <supplier-city-follows-part-city>
      <supplier id="S3" name="Blake" status="30" city="Paris"/>
      <part id="P1" name="Nut" color="Red" weight="12" city="London"/>
   </supplier-city-follows-part-city>
   <supplier-city-follows-part-city>
      <supplier id="S3" name="Blake" status="30" city="Paris"/>
      <part id="P4" name="Screw" color="Red" weight="14" city="London"/>
   </supplier-city-follows-part-city>
   <supplier-city-follows-part-city>
      <supplier id="S3" name="Blake" status="30" city="Paris"/>
      <part id="P6" name="Cog" color="Red" weight="19" city="London"/>
   </supplier-city-follows-part-city>
</result>
XSLT 2.0
<xsl:template match="/">
  <result>
    <xsl:for-each select="database/suppliers/*">
      <xsl:variable name="supplier" select="."/>
       <! This is okay in 2.0 >
      <xsl:for-each select="/database/parts/*[current( )/@city > @city]">
      <colocated>
        <xsl:copy-of select="$supplier"/>
        <xsl:copy-of select="."/>
      </colocated>
      </xsl:for-each>
    </xsl:for-each>
  </result>
</xsl:template>
6) Implementing the W3C XML Query-Use 
Cases in XSLT
1.	Use case "XMP": experiences and exemplars 
This use case contains several example queries that illustrate requirements gathered 
by the W3C from the database and document communities. The data use by these 
queries follows in Example 9-10 to Example 9-13. 
Example 9-10. bib.xml 
<bib>
    <book year="1994">
        <title>TCP/IP Illustrated</title>
        <author><last>Stevens</last><first>W.</first></author>
        <publisher>Addison-Wesley</publisher>
        <price> 65.95</price>
    </book>
 
    <book year="1992">
        <title>Advanced Programming in the Unix environment</title>
        <author><last>Stevens</last><first>W.</first></author>
        <publisher>Addison-Wesley</publisher>
        <price>65.95</price>
    </book>
 
    <book year="2000">
        <title>Data on the Web</title>
        <author><last>Abiteboul</last><first>Serge</first></author>
        <author><last>Buneman</last><first>Peter</first></author>
        <author><last>Suciu</last><first>Dan</first></author>
        <publisher>Morgan Kaufmann Publishers</publisher>
        <price> 39.95</price>
    </book>
 
    <book year="1999">
        <title>The Economics of Technology and Content for Digital TV</title>
        <editor>
               <last>Gerbarg</last><first>Darcy</first>
                <affiliation>CITI</affiliation>
        </editor>
            <publisher>Kluwer Academic Publishers</publisher>
        <price>129.95</price>
    </book>
 
</bib>
Example 9-11. reviews.xml 
reviews>
    <entry>
        <title>Data on the Web</title>
        <price>34.95</price>
        <review>
               A very good discussion of semi-structured database
               systems and XML.
        </review>
    </entry>
    <entry>
        <title>Advanced Programming in the Unix environment</title>
        <price>65.95</price>
        <review>
               A clear and detailed discussion of UNIX programming.
        </review>
    </entry>
    <entry>
        <title>TCP/IP Illustrated</title>
        <price>65.95</price>
        <review>
               One of the best books on TCP/IP.
        </review>
    </entry>
</reviews>
Example 9-12. books.xml 
<chapter>
    <title>Data Model</title>
    <section>
        <title>Syntax For Data Model</title>
    </section>
    <section>
        <title>XML</title>
        <section>
            <title>Basic Syntax</title>
        </section>
        <section>
            <title>XML and Semistructured Data</title>
        </section>
    </section>
</chapter>
Example 9-13. prices.xml 
<prices>
    <book>
        <title>Advanced Programming in the Unix environment</title>
        <source>www.amazon.com</source>
        <price>65.95</price>
    </book>
    <book>
        <title>Advanced Programming in the Unix environment </title>
        <source>www.bn.com</source>
        <price>65.95</price>
    </book>
    <book>
        <title> TCP/IP Illustrated </title>
        <source>www.amazon.com</source>
        <price>65.95</price>
    </book>
    <book>
        <title> TCP/IP Illustrated </title>
        <source>www.bn.com</source>
        <price>65.95</price>
    </book>
    <book>
        <title>Data on the Web</title>
        <source>www.amazon.com</source>
        <price>34.95</price>
    </book>
    <book>
        <title>Data on the Web</title>
        <source>www.bn.com</source>
        <price>39.95</price>
    </book>
</prices>
Question 1. List books in bib.xml published by Addison-Wesley after 1991, including 
their year and title: 
<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   
<xsl:import href="copy.xslt"/>
   
<xsl:template match="book[publisher = 'Addison-Wesley' and @year > 1991]">
  <xsl:copy-of select="."/>
</xsl:template>
   
<xsl:template match="book"/>
     
</xsl:stylesheet>
Question 2. Create a flat list of all the title-author pairs from bib.xml, with 
each pair enclosed in a "result" element: 
<xsl:template match="/">
<results>
  <xsl:apply-templates select="bib/book/author"/>
</results>
</xsl:template>
   
<xsl:template match="author">
  <result>
    <xsl:copy-of select="preceding-sibling::title"/>
    <xsl:copy-of select="."/>
  </result>
</xsl:template>
Question 3. For each book in bib.xml, list the title and authors, grouped inside a 
"result" element: 
<xsl:template match="bib">
  <results>
    <xsl:for-each select="book">
    <result>
      <xsl:copy-of select="title"/>
      <xsl:copy-of select="author"/>
    </result>
   </xsl:for-each>
  </results>
</xsl:template>
Question 4. For each author in bib.xml, list the author's name and the titles of 
all books by that author, grouped inside a "result" element: 
<xsl:template match="/">
<results>
  <xsl:for-each select="//author[not(.=preceding::author)]">
    <result>
      <xsl:copy-of select="."/>
      <xsl:for-each select="/bib/book[author=current()]">
        <xsl:copy-of select="title"/>
      </xsl:for-each>
    </result>
  </xsl:for-each>
  </results>
Question 5. For each book found on both http://www.bn.com (bib.xml) and 
http://www.amazon.com (reviews.xml), list the title of the book and its price from 
each source: 
<xsl:variable name="bn" select="document('bib.xml')"/>
<xsl:variable name="amazon" select="document('reviews.xml')"/>
   
<!--Solution 1 -->
<xsl:template match="/">
  <books-with-prices>
  <xsl:for-each select="$bn//book[title = $amazon//entry/title]">
    <book-with-prices>
      <xsl:copy-of select="title"/>
      <price-amazon><xsl:value-of 
      select="$amazon//entry[title=current()/title]/price"/></price-amazon>
      <price-bn><xsl:value-of select="price"/></price-bn>
    </book-with-prices>
  </xsl:for-each>
  </books-with-prices>
</xsl:template>
   
<!--Solution 2-->
<xsl:template match="/">
  <books-with-prices>
  <xsl:for-each select="$bn//book">
    <xsl:variable name="bn-book" select="."/>
    <xsl:for-each select="$amazon//entry[title=$bn-book/title]">
      <book-with-prices>
        <xsl:copy-of select="title"/>
        <price-amazon><xsl:value-of select="price"/></price-amazon>
        <price-bn><xsl:value-of select="$bn-book/price"/></price-bn>
      </book-with-prices>
    </xsl:for-each>
  </xsl:for-each>
  </books-with-prices>
</xsl:template>
Question 6. For each book that has at least one author, list the title and first 
two authors, as well as an empty "et-al" element if the book has additional 
authors: 
<xsl:template match="bib">
  <xsl:copy>
    <xsl:for-each select="book[author]">
      <xsl:copy>
        <xsl:copy-of select="title"/>
        <xsl:copy-of select="author[position() &lt;= 2]"/>
        <xsl:if test="author[3]">
        <et-al/>
        </xsl:if>
      </xsl:copy>
    </xsl:for-each>
  </xsl:copy>
</xsl:template>
Question 7. List the titles and years of all books published by Addison-Wesley 
after 1991, in alphabetic order: 
<xsl:template match="bib">
  <xsl:copy>
    <xsl:for-each select="book[publisher = 'Addison-Wesley' 
          and @year > 1991]">
      <xsl:sort select="title"/>
      <xsl:copy>
        <xsl:copy-of select="@year"/>
        <xsl:copy-of select="title"/>
      </xsl:copy>
    </xsl:for-each>
  </xsl:copy>
</xsl:template>
Question 8. In the document books.xml, find all section or chapter titles that 
contain the word "XML", regardless of the nesting level: 
 <xsl:template match="/">
<results>
  <xsl:copy-of select="(//chapter/title | 
  //section/title)[contains(.,'XML')]"/>
</results>
</xsl:template>
Question 9. In the document prices.xml, find the minimum price for each book in the 
form of a "minprice" element with the book title as its title attribute: 
<xsl:include href="../math/math.min.xslt"/>
   
<xsl:template match="/">
<results>
  <xsl:for-each select="//book/title[not(. = ./preceding::title)]">
    <xsl:variable name="min-price">
      <xsl:call-template name="math:min">
        <xsl:with-param name="nodes" select="//book[title = 
                                        current()]/price"/>
      </xsl:call-template>
    </xsl:variable>
    <minprice title="{.}">
      <price><xsl:value-of select="$min-price"/></prices>
    </minprice>
  </xsl:for-each>
</results>
</xsl:template>
Question 10. For each book with an author, return the book with its title and 
authors. For each book with an editor, return a reference with the book title and 
the editor's affiliation: 
<xsl:template match="bib">
<xsl:copy>
  <xsl:for-each select="book[author]">
    <xsl:copy>
      <xsl:copy-of select="title"/>
      <xsl:copy-of select="author"/>
    </xsl:copy>
  </xsl:for-each>
 
  <xsl:for-each select="book[editor]">
    <reference>
      <xsl:copy-of select="title"/>
      <org><xsl:value-of select="editor/affiliation"/></org>
    </reference>
  </xsl:for-each>
  </xsl:copy>
</xsl:template>
Question 11. Find pairs of books that have different titles but the same set of 
authors (possibly in a different order): 
<xsl:include href="query.equal-values.xslt"/>
   
<xsl:template match="bib">
  <xsl:copy>
    <xsl:for-each select="book[author]">
      <xsl:variable name="book1" select="."/>
      <xsl:for-each select="./following-sibling::book[author]">
        <xsl:variable name="same-authors">
          <xsl:call-template name="query:equal-values">
            <xsl:with-param name="nodes1" select="$book1/author"/>
            <xsl:with-param name="nodes2" select="author"/>
          </xsl:call-template>
        </xsl:variable>
        <xsl:if test="string($same-authors)">
          <book-pair>
            <xsl:copy-of select="$book1/title"/>
            <xsl:copy-of select="title"/>
          </book-pair>
        </xsl:if>
      </xsl:for-each>
    </xsl:for-each>
  </xsl:copy>
</xsl:template>
 
Example 9-14. book.dtd 
<!ELEMENT book (title, author+, section+)>
  <!ELEMENT title (#PCDATA)>
  <!ELEMENT author (#PCDATA)>
  <!ELEMENT section (title, (p | figure | section)* )>
  <!ATTLIST section
      id         ID    #IMPLIED
      difficulty CDATA #IMPLIED>
  <!ELEMENT p (#PCDATA)>
  <!ELEMENT figure (title, image)>
  <!ATTLIST figure
       width   CDATA   #REQUIRED
       height  CDATA   #REQUIRED >
  <!ELEMENT image EMPTY>
  <!ATTLIST image
       source  CDATA   #REQUIRED >
Example 9-15. book.xml 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE book SYSTEM "book.dtd">
<book>
  <title>Data on the Web</title>
  <author>Serge Abiteboul</author>
  <author>Peter Buneman</author>
  <author>Dan Suciu</author>
  <section id="intro" difficulty="easy" >
    <title>Introduction</title>
    <p>Text ... </p>
    <section>
      <title>Audience</title>
      <p>Text ... </p>
    </section>
    <section>
      <title>Web Data and the Two Cultures</title>
      <p>Text ... </p>
      <figure height="400" width="400">
        <title>Traditional client/server architecture</title>
        <image source="csarch.gif"/>
      </figure>
      <p>Text ... </p>
    </section>
  </section>
  <section id="syntax" difficulty="medium" >
    <title>A Syntax For Data</title>
    <p>Text ... </p>
    <figure height="200" width="500">
      <title>Graph representations of structures</title>
      <image source="graphs.gif"/>
    </figure>
    <p>Text ... </p>
    <section>
      <title>Base Types</title>
      <p>Text ... </p>
    </section>
    <section>
      <title>Representing Relational Databases</title>
      <p>Text ... </p>
      <figure height="250" width="400">
        <title>Examples of Relations</title>
        <image source="relations.gif"/>
      </figure>
    </section>
    <section>
      <title>Representing Object Databases</title>
      <p>Text ... </p>
    </section>       
  </section>
</book>
Question 1. Prepare a (nested) table of contents for Book1, listing all the 
sections and their titles. Preserve the original attributes of each <section> 
element, if any exist: 
<xsl:template match="book">
  <toc>
    <xsl:apply-templates/>
  </toc>
</xsl:template>
   
<!-- Copy element of toc -->
<xsl:template match="section | section/title | section/title/text()">
  <xsl:copy>
    <xsl:copy-of select="@*"/>
      <xsl:apply-templates/>
  </xsl:copy>
</xsl:template>
   
<!-- Supress other elements -->
<xsl:template match="* | text()"/>
Question 2. Prepare a (flat) figure list for Book1, listing all figures and their 
titles. Preserve the original attributes of each <figure> element, if any exist: 
<xsl:template match="book">
  <figlist>
    <xsl:for-each select=".//figure">
      <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:copy-of select="title"/>
      </xsl:copy>
    </xsl:for-each>
  </figlist>
</xsl:template>
Question 3. How many sections are in Book1, and how many figures? 
<xsl:template match="/">
  <section-count><xsl:value-of select="count(//section)"/></section-count>
  <figure-count><xsl:value-of select="count(//figure)"/></figure-count>
</xsl:template>
Question 4. How many top-level sections are in Book1?
<xsl:template match="book">
  <top_section_count>
     <xsl:value-of select="count(section)"/>
  </top_section_count>
</xsl:template>
Question 5. Make a flat list of the section elements in Book1. In place of its 
original attributes, each section element should have two attributes, containing 
the title of the section and the number of figures immediately contained in the 
section: 
<xsl:template match="book">
<section_list>
  <xsl:for-each select=".//section">
    <section title="{title}" figcount="{count(figure)}"/>
  </xsl:for-each>
</section_list>
</xsl:template>
Question 6. Make a nested list of the section elements in Book1, preserving their 
original attributes and hierarchy. Inside each section element, include the title 
of the section and an element that includes the number of figures immediately 
contained in the section. See Example 9-16 and Example 9-17. 
Example 9-16. The solution as I would interpret the English requirements 
<xsl:template match="book">
<toc>
  <xsl:apply-templates select="section"/>
</toc>
</xsl:template>
   
<xsl:template match="section">
  <xsl:copy>
    <xsl:copy-of select="@*"/>
    <xsl:copy-of select="title"/>
    <figcount><xsl:value-of select="count(figure)"/></figcount>
    <xsl:apply-templates select="section"/>
  </xsl:copy>
</xsl:template>
Example 9-17. What the W3C use case wants based on a sample result and XQuery 
<xsl:template match="book">
<toc>
  <xsl:for-each select="//section">
    <xsl:sort select="count(ancestor::section)"/>
    <xsl:apply-templates select="."/>
  </xsl:for-each>
</toc>
</xsl:template>
   
<xsl:template match="section">
  <xsl:copy>
    <xsl:copy-of select="@*"/>
    <xsl:copy-of select="title"/>
    <figcount><xsl:value-of select="count(figure)"/></figcount>
    <xsl:apply-templates select="section"/>
  </xsl:copy>
</xsl:template>
1.	Use case "SEQ": queries based on sequence. 
<!DOCTYPE report [
  <!ELEMENT report (section*)>
  <!ELEMENT section (section.title, section.content)>
  <!ELEMENT section.title  (#PCDATA )>
  <!ELEMENT section.content  (#PCDATA | anesthesia | prep 
                            | incision | action | observation )*>
  <!ELEMENT anesthesia (#PCDATA)>
  <!ELEMENT prep ( (#PCDATA | action)* )>
  <!ELEMENT incision ( (#PCDATA | geography | instrument)* )>
  <!ELEMENT action ( (#PCDATA | instrument )* )>
  <!ELEMENT observation (#PCDATA)>
  <!ELEMENT geography (#PCDATA)>
  <!ELEMENT instrument (#PCDATA)>
]> 
<report>
  <section>
    <section.title>Procedure</section.title>
     <section.content>
      The patient was taken to the operating room where she was placed
      in supine position and
      <anesthesia>induced under general anesthesia.</anesthesia>
      <prep> 
        <action>A Foley catheter was placed to decompress the bladder</action>
        and the abdomen was then prepped and draped in sterile fashion.
      </prep>  
      <incision>
        A curvilinear incision was made
        <geography>in the midline immediately infraumbilical</geography>
        and the subcutaneous tissue was divided
        <instrument>using electrocautery.</instrument>
      </incision>
      The fascia was identified and
      <action>#2 0 Maxon stay sutures were placed on each side of the midline.
      </action>
      <incision>
        The fascia was divided using
        <instrument>electrocautery</instrument>
        and the peritoneum was entered.
      </incision>
      <observation>The small bowel was identified.</observation>
      and
      <action>
        the
        <instrument>Hasson trocar</instrument>
        was placed under direct visualization.
      </action>
      <action>
        The
        <instrument>trocar</instrument>
        was secured to the fascia using the stay sutures.
      </action>
     </section.content>
  </section>
</report>
Question 1. In the Procedure section of Report1, what instruments were used in the 
second incision? 
<xsl:template match="section[section.title = 'Procedure']">
<xsl:copy-of select="(.//incision)[2]/instrument"/>
</xsl:template>
Question 2. In the Procedure section of Report1, what are the first two instruments 
to be used? 
<xsl:template match="section[section.title = 'Procedure']">
<xsl:copy-of select="(.//instrument)[position() &lt;= 2]"/>
</xsl:template>
Question 3. In Report1, what instruments were used in the first two actions after 
the second incision? 
<xsl:template match="report">
<!-- i2 = Second incision in the entire report -->
<xsl:variable name="i2" select="(.//incision)[2]"/>
<!-- Of all the actions following i2 
     get the instruments used in the first two -->
<xsl:copy-of 
     select="($i2/following::action)[position() &lt;= 2]/instrument"/>
</xsl:template>
Question 4. In Report1, find "Procedure" sections for which no anesthesia element 
occurs before the first incision: 
<xsl:template match="section[section.title = 'Procedure']">
  <xsl:variable name="i1" select="(.//incision)[1]"/>
  <xsl:if test=".//anesthesia[preceding::incision = $i1]">
    <xsl:copy-of select="current()"/>
  </xsl:if> 
</xsl:template>
If the result is not empty then a major lawsuit is soon to follow!
Question 5. In Report1, what happened between the first and second incision? 
<xsl:template match="report">
<critical_sequence>
  <!-- i1 = First incision in the entire report -->
  <xsl:variable name="i1" select="(.//incision)[1]"/>
  <!-- i2 = Second incision in the entire report -->
  <xsl:variable name="i2" select="(.//incision)[2]"/>
  <!-- copy all sibling nodes following i1 
     that don't have a preceding element i2 and are not themeseves i2 -->
  <xsl:for-each select="$i1/following-sibling::node() 
               [not(./preceding::incision = $i2) and not(. = $i2)]">
    <xsl:copy-of select="."/>
  </xsl:for-each>
</critical_sequence> 
</xsl:template>
In Questions 4 and 5, I assume that the string values of incision 
elements are unique. This is true in the sample data, but may not be true 
in the most general case. To be precise, you should apply Recipe 4.2. 
For example, in Question 4, the test should be:
test=".//anesthesia[count(./preceding::incision | 
$i1) = count(./preceding::incision)]"
1.	Use case "R": access to relational data. 
Example 9-18. users.xml 
<users>
  <user_tuple>
    <userid>U01</userid>
    <name>Tom Jones</name>
    <rating>B</rating>
  </user_tuple>
  <user_tuple>
    <userid>U02</userid>
    <name>Mary Doe</name>
    <rating>A</rating>
  </user_tuple>
  <user_tuple>
    <userid>U03</userid>
    <name>Dee Linquent</name>
    <rating>D</rating>
  </user_tuple>
  <user_tuple>
    <userid>U04</userid>
    <name>Roger Smith</name>
    <rating>C</rating>
  </user_tuple>
  <user_tuple>
    <userid>U05</userid>
    <name>Jack Sprat</name>
    <rating>B</rating>
  </user_tuple>
  <user_tuple>
    <userid>U06</userid>
    <name>Rip Van Winkle</name>
    <rating>B</rating>
  </user_tuple>
</users>
Example 9-19. items.xml 
<items>
  <item_tuple>
    <itemno>1001</itemno>
    <description>Red Bicycle</description>
    <offered_by>U01</offered_by>
    <start_date>99-01-05</start_date>
    <end_date>99-01-20</end_date>
    <reserve_price>40</reserve_price>
  </item_tuple>
  <item_tuple>
    <itemno>1002</itemno>
    <description>Motorcycle</description>
    <offered_by>U02</offered_by>
    <start_date>99-02-11</start_date>
    <end_date>99-03-15</end_date>
    <reserve_price>500</reserve_price>
  </item_tuple>
  <item_tuple>
    <itemno>1003</itemno>
    <description>Old Bicycle</description>
    <offered_by>U02</offered_by>
    <start_date>99-01-10</start_date>
    <end_date>99-02-20</end_date>
    <reserve_price>25</reserve_price>
  </item_tuple>
  <item_tuple>
    <itemno>1004</itemno>
    <description>Tricycle</description>
    <offered_by>U01</offered_by>
    <start_date>99-02-25</start_date>
    <end_date>99-03-08</end_date>
    <reserve_price>15</reserve_price>
  </item_tuple>
  <item_tuple>
    <itemno>1005</itemno>
    <description>Tennis Racket</description>
    <offered_by>U03</offered_by>
    <start_date>99-03-19</start_date>
    <end_date>99-04-30</end_date>
    <reserve_price>20</reserve_price>
  </item_tuple>
  <item_tuple>
    <itemno>1006</itemno>
    <description>Helicopter</description>
    <offered_by>U03</offered_by>
    <start_date>99-05-05</start_date>
    <end_date>99-05-25</end_date>
    <reserve_price>50000</reserve_price>
  </item_tuple>
  <item_tuple>
    <itemno>1007</itemno>
    <description>Racing Bicycle</description>
    <offered_by>U04</offered_by>
    <start_date>99-01-20</start_date>
    <end_date>99-02-20</end_date>
    <reserve_price>200</reserve_price>
  </item_tuple>
  <item_tuple>
    <itemno>1008</itemno>
    <description>Broken Bicycle</description>
    <offered_by>U01</offered_by>
    <start_date>99-02-05</start_date>
    <end_date>99-03-06</end_date>
    <reserve_price>25</reserve_price>
  </item_tuple>
</items>
Example 9-20. bids.xml 
<bids>
  <bid_tuple>
    <userid>U02</userid>
    <itemno>1001</itemno>
    <bid> 35</bid>
    <bid_date>99-01-07 </bid_date>
  </bid_tuple>
  <bid_tuple>
    <userid>U04</userid>
    <itemno>1001</itemno>
    <bid>40</bid>
    <bid_date>99-01-08</bid_date>
  </bid_tuple>
  <bid_tuple>
    <userid>U02</userid>
    <itemno>1001 </itemno>
    <bid>45</bid>
    <bid_date>99-01-11</bid_date>
  </bid_tuple>
  <bid_tuple>
    <userid>U04</userid>
    <itemno>1001</itemno>
    <bid>50</bid>
    <bid_date>99-01-13</bid_date>
  </bid_tuple>
  <bid_tuple>
    <userid>U02</userid>
    <itemno>1001</itemno>
    <bid>55</bid>
    <bid_date>99-01-15</bid_date>
  </bid_tuple>
  <bid_tuple>
    <userid>U01</userid>
    <itemno>1002</itemno>
    <bid>400</bid>
    <bid_date>99-02-14</bid_date>
  </bid_tuple>
  <bid_tuple>
    <userid>U02</userid>
    <itemno>1002</itemno>
    <bid>600</bid>
    <bid_date>99-02-16</bid_date>
  </bid_tuple>
  <bid_tuple>
    <userid>U03</userid>
    <itemno>1002</itemno>
    <bid>800</bid>
    <bid_date>99-02-17</bid_date>
  </bid_tuple>
  <bid_tuple>
    <userid>U04</userid>
    <itemno>1002</itemno>
    <bid>1000</bid>
    <bid_date>99-02-25</bid_date>
  </bid_tuple>
  <bid_tuple>
    <userid>U02</userid>
    <itemno>1002</itemno>
    <bid>1200</bid>
    <bid_date>99-03-02</bid_date>
  </bid_tuple>
  <bid_tuple>
    <userid>U04</userid>
    <itemno>1003</itemno>
    <bid>15</bid>
    <bid_date>99-01-22</bid_date>
  </bid_tuple>
  <bid_tuple>
    <userid>U05</userid>
    <itemno>1003</itemno>
    <bid>20</bid>
    <bid_date>99-02-03</bid_date>
  </bid_tuple>
  <bid_tuple>
    <userid>U01</userid>
    <itemno>1004</itemno>
    <bid>40</bid>
    <bid_date>99-03-05</bid_date>
  </bid_tuple>
  <bid_tuple>
    <userid>U03</userid>
    <itemno>1007</itemno>
    <bid>175</bid>
    <bid_date>99-01-25</bid_date>
  </bid_tuple>
  <bid_tuple>
    <userid>U05</userid>
    <itemno>1007</itemno>
    <bid>200</bid>
    <bid_date>99-02-08</bid_date>
  </bid_tuple>
  <bid_tuple>
    <userid>U04</userid>
    <itemno>1007</itemno>
    <bid>225</bid>
    <bid_date>99-02-12</bid_date>
  </bid_tuple>
</bids>
Question 1. List the item number and description of all bicycles that currently 
have an auction in progress, ordered by item number: 
<xsl:include href="../date/date.date-time.xslt"/>
   
<!-- To make the result come out like the W3C example -->
<xsl:param name="today" select="'1999-01-21'"/>
   
<xsl:template match="items">
   
  <xsl:variable name="today-abs">
    <xsl:call-template name="date:date-to-absolute-day">
      <xsl:with-param name="date" select="$today"/>
    </xsl:call-template>
  </xsl:variable> 
   
<result>
  <xsl:for-each select="item_tuple">
    <xsl:sort select="itemno" data-type="number"/>
    
    <xsl:variable name="start-abs">
      <xsl:call-template name="date:date-to-absolute-day">
        <xsl:with-param name="date" select="start_date"/>
      </xsl:call-template>
    </xsl:variable>
    
    <xsl:variable name="end-abs">
      <xsl:call-template name="date:date-to-absolute-day">
        <xsl:with-param name="date" select="end_date"/>
      </xsl:call-template>
    </xsl:variable>
   
    <xsl:if test="$start-abs &lt;= $today-abs and $end-abs >= 
        $today-abs and contains(description, 'Bicycle')">
      <xsl:copy>
        <xsl:copy-of select="itemno"/>
        <xsl:copy-of select="description"/>
      </xsl:copy>
    </xsl:if>
    
  </xsl:for-each>
</result>
</xsl:template>
Question 2. For all bicycles, list the item number, description, and highest bid 
(if any), ordered by item number: 
<xsl:include href="../math/math.max.xslt"/>
   
<xsl:template match="items">
   
<result>
  <xsl:for-each select="item_tuple[contains(description,'Bicycle')]">
    <xsl:sort select="itemno" data-type="number"/>
  
  <xsl:variable name="bids" 
     select="document('bids.xml')//bid_tuple[itemno=current()/itemno]/bid"/>
   
  <xsl:variable name="high-bid">
    <xsl:call-template name="math:max">
      <xsl:with-param name="nodes" select="$bids"/>
    </xsl:call-template>
  </xsl:variable> 
  
  <xsl:copy>
    <xsl:copy-of select="itemno"/>
    <xsl:copy-of select="description"/>
    <high_bid><xsl:if test="$bids"><xsl:value-of 
          select="$high-bid"/></xsl:if></high_bid>
  </xsl:copy>
    
  </xsl:for-each>
</result>
</xsl:template>
Question 3. Find cases when a user with a rating worse (alphabetically, greater) 
than "C" offers an item with a reserve price of more than 1,000: 
<!-- Not strictly nec. but spec does not define ratings system so we derive 
it dynamically! -->
<xsl:variable name="ratings">
  <xsl:for-each select="document('users.xml')//user_tuple/rating">
    <xsl:sort select="." data-type="text"/>
    <xsl:if test="not(. = ./preceding::rating)">
      <xsl:value-of select="."/>
    </xsl:if>
  </xsl:for-each>
</xsl:variable>
   
<xsl:template match="items">
<result>
  <xsl:for-each select="item_tuple[reserve_price > 1000]">
   
  <xsl:variable name="user" select="document('users.xml')//user_tuple[userid 
  = current()/offered_by]"/>
   
  <xsl:if test="string-length(substring-before($ratings,$user/rating)) >
  string-length(substring-before($ratings,'C'))">
    <warning>
      <xsl:copy-of select="$user/name"/>
      <xsl:copy-of select="$user/rating"/>
      <xsl:copy-of select="description"/>
      <xsl:copy-of select="reserve_price"/>
    </warning>
  </xsl:if>    
  </xsl:for-each>
</result>
</xsl:template>
Question 4. List item numbers and descriptions of items that have no bids:
<xsl:template match="items">
<result>
  <xsl:for-each select="item_tuple">
   
  <xsl:if test="not(document('bids.xml')//bid_tuple[itemno = 
  current()/itemno])">
    <no_bid_item>
      <xsl:copy-of select="itemno"/>
      <xsl:copy-of select="description"/>
    </no_bid_item>
  </xsl:if>    
  
  </xsl:for-each>
</result>
</xsl:template>
1.	Use case "TEXT": full-text search. 
Question 1. Find all news items in which the name "Foobar Corporation" appears in 
the title: 
<xsl:template match="news">
<result>
  <xsl:copy-of select="news_item/title[contains(., 'Foobar Corporation')]"/>
</result>
</xsl:template>
Question 2. For each news item that is relevant to the Gorilla Corporation, create 
an "item summary" element. The content of the item summary is the title, date, and 
first paragraph of the news item, separated by periods. A news item is relevant if 
the name of the company is mentioned anywhere within the content of the news item: 
<xsl:template match="news">
<result>
  <xsl:for-each select="news_item[contains(content,'Gorilla Corporation')]">
    <item_summary>
      <xsl:value-of select="normalize-space(title)"/>. <xsl:text/>
      <xsl:value-of select="normalize-space(date)"/>. <xsl:text/>
      <xsl:value-of select="normalize-space(content/par[1])"/>
   </item_summary>
  </xsl:for-each>
</result>
</xsl:template>
1.	Use case "PARTS": recursive parts explosion 
<!DOCTYPE partlist [
    <!ELEMENT partlist (part*)>
    <!ELEMENT part EMPTY>
    <!ATTLIST part
          partid  CDATA  #REQUIRED
          partof  CDATA  #IMPLIED
          name    CDATA  #REQUIRED>
]>

<!DOCTYPE parttree [
    <!ELEMENT parttree (part*)>
    <!ELEMENT part (part*)>
    <!ATTLIST part
          partid  CDATA  #REQUIRED
          name    CDATA  #REQUIRED>
]>
Sample data conforming to that DTD might look like this:
<?xml version="1.0" encoding="ISO-8859-1"?>
<partlist>
  <part partid="0" name="car"/>
  <part partid="1" partof="0" name="engine"/>
  <part partid="2" partof="0" name="door"/>
  <part partid="3" partof="1" name="piston"/>
  <part partid="4" partof="2" name="window"/>
  <part partid="5" partof="2" name="lock"/>
  <part partid="10" name="skateboard"/>
  <part partid="11" partof="10" name="board"/>
  <part partid="12" partof="10" name="wheel"/>
  <part partid="20" name="canoe"/>
</partlist>
Question 1. Convert the sample document from "partlist" to "parttree" format (see 
the DTD section for definitions). In the result document, part containment is 
represented by containment of one <part> element inside another. Each part that is 
not part of any other part should appear as a separate top-level element in the 
output document: 
<xsl:template match="partlist">
  <parttree>
     <!-- Start with the part that is not part of anything -->
    <xsl:apply-templates select="part[not(@partof)]"/>
  </parttree>
</xsl:template>
   
<xsl:template match="part">
  <part partid="{@partid}" name="{@name}">
    <xsl:apply-templates select="../part[@partof = current()/@partid]"/>
  </part>
</xsl:template>
It turns out that this sort of transformation is easier to code and understand in 
XSLT than in XQuery. For comparison, here is the XQuery solution offered by 
the W3C paper: 
define function one_level (element $p) returns element
{
    <part partid="{ $p/@partid }"
          name="{ $p/@name }" >
        {
            for $s in document("partlist.xml")//part
            where $s/@partof = $p/@partid
            return one_level($s)
        }
    </part>
}
   
<parttree>
  {
    for $p in document("partlist.xml")//part[empty(@partof)]
    return one_level($p)
  }
</parttree>
1.	Use case "REF": queries based on references.  
 
<!DOCTYPE census [
  <!ELEMENT census (person*)>
  <!ELEMENT person (person*)>
  <!ATTLIST person
        name    ID      #REQUIRED
        spouse  IDREF   #IMPLIED
        job     CDATA   #IMPLIED >
]>

<census>
  <person name="Bill" job="Teacher">
    <person name="Joe" job="Painter" spouse="Martha">
      <person name="Sam" job="Nurse">
        <person name="Fred" job="Senator" spouse="Jane">
        </person>
      </person>
      <person name="Karen" job="Doctor" spouse="Steve">
      </person>
    </person>
    <person name="Mary" job="Pilot">
      <person name="Susan" job="Pilot" spouse="Dave">
      </person>
    </person>
  </person>
  <person name="Frank" job="Writer">
    <person name="Martha" job="Programmer" spouse="Joe">
      <person name="Dave" job="Athlete" spouse="Susan">
      </person>
    </person>
    <person name="John" job="Artist">
      <person name="Helen" job="Athlete">
      </person>
      <person name="Steve" job="Accountant" spouse="Karen">
        <person name="Jane" job="Doctor" spouse="Fred">
        </person>
      </person>
    </person>
  </person>
</census>
Question 1. Find Martha's spouse:
<xsl:strip-space elements="*"/>
   
<xsl:template match="person[@spouse='Martha']">
  <xsl:copy>
    <xsl:copy-of select="@*"/>
  </xsl:copy>
</xsl:template>
Question 2. Find parents of athletes:
<xsl:template match="census">
  <xsl:variable name="everyone"  select="//person"/>
  <result>
     <!-- For each person with children -->
    <xsl:for-each select="$everyone[person]">
      <xsl:variable name="spouse" 
          select="$everyone[@spouse=current()/@name]"/>
      <xsl:if test="./person/@job = 'Athlete' or 
                 $spouse/person/@job = 'Athlete'">
        <xsl:copy>
          <xsl:copy-of select="@*"/>
        </xsl:copy>
      </xsl:if>
    </xsl:for-each>
  </result>
</xsl:template>

Question 4. List names of parents and children who have the same job, and list 
their jobs: 
<xsl:template match="census">
  <xsl:variable name="everyone"  select="//person"/>
  <result>
     <!-- For each person with children -->
    <xsl:for-each select="$everyone[person]">
   
      <xsl:variable name="spouse" 
          select="$everyone[@spouse=current()/@name]"/>
   
      <xsl:apply-templates select="person[@job = current()/@job]">
        <xsl:with-param name="parent" select="@name"/>
      </xsl:apply-templates>
   
      <xsl:apply-templates select="person[@job = $spouse/@job]">
        <xsl:with-param name="parent" select="$spouse/@name"/>
      </xsl:apply-templates>
      
    </xsl:for-each>
  </result>
</xsl:template>
   
<xsl:template match="person">
  <xsl:param name="parent"/>
  <match parent="{$parent}"  child="{@name}" job="{@job}"/>
</xsl:template>
Question 5. List name-pairs of grandparents and grandchildren:
<xsl:template match="census">
  <xsl:variable name="everyone"  select="//person"/>
  <result>
    <!-- For each grandchild -->
    <xsl:for-each select="$everyone[../../../person]">
        <!-- Get the grandparent1 (guaranteed to exist by for each -->
        <grandparent name="{../../@name}" grandchild="{@name}"/>
        <!-- Get the grandparent2 is grandparent1's  spouse if listed -->
        <xsl:if test="../../@spouse">
          <grandparent name="{../../@spouse}" grandchild="{@name}"/>
        </xsl:if>
       <!-- Get the names of this person's parent's spouse 
          (i.e. their mother or father as the case may be) --> 
        <xsl:variable name="spouse-of-parent" select="../@spouse"/>
        <!-- Get parents of spouse-of-parent, if present -->
        <xsl:variable name="gp3" 
          select="$everyone[person/@name=$spouse-of-parent]"/>
        <xsl:if test="$gp3">
          <grandparent name="{$gp3/@name}" grandchild="{@name}"/>
          <xsl:if test="$gp3/@spouse">
            <grandparent name="{$gp3/@spouse}" grandchild="{@name}"/>
          </xsl:if>
        </xsl:if>
    </xsl:for-each>
  </result>
</xsl:template>
Question 6. Find people with no children:
<xsl:strip-space elements="*"/>
<xsl:template match="census">
  <xsl:variable name="everyone"  select="//person"/>
  <result>
    <xsl:for-each select="$everyone[not(./person)]">
      <xsl:variable name="spouse" 
      select="$everyone[@name = current()/@spouse]"/>
      <xsl:if test="not ($spouse) or not($spouse/person)">
        <xsl:copy-of select="."/>
      </xsl:if>
    </xsl:for-each>
  </result>
</xsl:template>
Question 7. List the names of all Joe's descendants. Show each descendant as an 
element with the descendant's name as content and his or her marital status and 
number of children as attributes. Sort the descendants in descending order by 
number of children and secondarily in alphabetical order by name: 
<xsl:variable name="everyone" select="//person"/>
   
<xsl:template match="census">
  <result>
    <xsl:apply-templates select="//person[@name='Joe']"/>
  </result>
</xsl:template>
   
<xsl:template match="person">
  
  <xsl:variable name="all-desc">
    <xsl:call-template name="descendants">
      <xsl:with-param name="nodes" select="."/>
    </xsl:call-template>
  </xsl:variable>
    
  <xsl:for-each select="exsl:node-set($all-desc)/*">
    <xsl:sort select="count(./* | $everyone[@name = current()/@spouse]/*)" 
    order="descending" data-type="number"/>
    <xsl:sort select="@name"/>
    <xsl:variable name="mstatus" 
          select="normalize-space(
               substring('No Yes',boolean(@spouse)* 3+1,3))"/>
    <person married="{$mstatus}" 
          nkids="{count(./* | $everyone[@name = current()/@spouse]/*)}">
          <xsl:value-of select="@name"/>
     </person> 
  </xsl:for-each>
</xsl:template>
   
<xsl:template name="descendants">
  <xsl:param name="nodes"/>
  <xsl:param name="descendants" select="/.."/>
  
  <xsl:choose>
    <xsl:when test="not($nodes)">
      <xsl:copy-of select="$descendants"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="descendants">
        <xsl:with-param name="nodes" select="$nodes[position() > 1] | 
          $nodes[1]/person | id($nodes[1]/@spouse)/person"/>
        <xsl:with-param name="descendants" select="$descendants | 
          $nodes[1]/person | id($nodes[1]/@spouse)/person"/>
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
  
</xsl:template>
define function descrip (element $e) returns element
{
    let $kids := $e/* union $e/@spouse=>person/*
    let $mstatus :=  if ($e[@spouse]) then "Yes" else "No"
    return
        <person married={ $mstatus } nkids={ count($kids) }>{ $e/@name/text() } 
        </person>

   
define function descendants (element $e)
{
    if (empty($e/* union $e/@spouse=>person/*))
    then $e
    else $e union descendants($e/* union $e/@spouse=>person/*)
}
   
descrip(descendants(//person[@name = "Joe"])) sortby(@nkids descending, .)
Discussion
XSLT 2.0

Question 4. For each author in bib.xml, list the author's name and the titles of 
all books by that author, grouped inside a "result" element: 

  <xsl:for-each-group select="//author" group-by=".">
    <result>
      <xsl:copy-of select="."/>
      <xsl:for-each select="/bib/book[author eq current-grouping-key()]">
        <xsl:copy-of select="title"/>
      </xsl:for-each>
    </result>
  </xsl:for-each-group>

<!-- Using distinct-values() -->

  <xsl:for-each select="distinct-values(//author)">
    <result>
      <xsl:copy-of select="."/>
      <xsl:for-each select="/bib/book[author eq .]">
        <xsl:copy-of select="title"/>
      </xsl:for-each>
    </result>
  </xsl:for-each>
*	Avoid copying nodes by using xsl:sequence
Question 8. In the document books.xml, find all section or chapter titles that 
contain the word "XML", regardless of the nesting level: 
<results>
  <xsl:sequence select="(//chapter | //section)/title)[contains(.,'XML')]"/>
</results> 
*	Take advantage of functions and sequences to simplify queries.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:xs="http://www.w3.org/2001/XMLSchema" 
xmlns:ckbk="http://www.ora.com/XSLTCkbk">

<xsl:key name="person-key" match="person" use="@name"/>
	
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:variable name="everyone" select="//person" as="item()*"/>
   
<xsl:template match="census">
  <result>
    <xsl:apply-templates select="//person[@name='Joe']"/>
  </result>
</xsl:template>
   
<xsl:template match="person">
  
  <!--No need for node set conversions. Use descendants function directly -->  
  <xsl:for-each select="ckbk:descendants(.,/)[current() != .]">
    <xsl:sort select="count(./* | $everyone[@name = current()/@spouse]/*)" 
    order="descending" data-type="number"/>
    <xsl:sort select="@name"/>
    <xsl:variable name="mstatus" select="if (@spouse) then 'Yes'   else 'No'"/>
    <person married="{$mstatus}" 
          nkids="{count(./* | key('person-key', @spouse)/*)}">
          <xsl:value-of select="@name"/>
     </person> 
  </xsl:for-each>
</xsl:template>
   
<!-- Note how this function is simplier than the template in the XSLT 1.0 solution 
We pass in the doc because it is unkown inside of functions.-->
<xsl:function  name="ckbk:descendants">
  <xsl:param name="nodes" as="item()*"/>
  <xsl:param name="doc"/>
  <xsl:sequence select="$nodes, for $person in $nodes return ckbk:descendants( 
($person/person, key('person-key', $person/@spouse,$doc)/person), $doc)"/>
</xsl:function>

</xsl:stylesheet>

 
 
 
 
40		Querying XML
6) Implementing the W3C XML Query-Use Cases in XSLT		39 of 41
40
	39
DRAFT	O'Reilly & Associates	1/16/2006
